04

您所在的位置:网站首页 docker run cmd 区别 04

04

#04| 来源: 网络整理| 查看: 265

04_Docker 镜像和仓库

文章目录 04_Docker 镜像和仓库4.1 什么是 Docker 镜像4.2 列出 Docker 镜像4.3 拉取镜像4.4 查找镜像4.5 构建镜像4.5.1 创建 Docker Hub 账号4.5.2 用 Docker 的 commit 命令创建镜像4.5.3 用 Dockerfile 构建镜像4.5.5 基于 Dockerfile 构建新镜像4.5.5 指令失败时会怎样4.5.6 Dockerfile 和构建缓存4.5.7 基于构建缓存的 Dockerfile 模板4.5.8 查看新镜像4.5.9 从新镜像启动容器4.5.10 Dockerfile 指令1.CMD2.ENTRYPOINT3.WORDIR4.ENV5.USER6.VOLUME7.ADD8.COPY9.LABEL10.STOPSIGNAL11.ARG12.ONBUILD 4.6 将镜像推送到 Docker Hub4.7 删除镜像4.8 运行自己的 Docker Registry4.9 其他可选 Registry 服务 在第二章,学习了如何安装 Docker,在第三章学习了包括 docker run 在内的很多管理 Docker 容器的命令。

回顾一下 docker run 命令

sudo docker run -i -t --name my_container ubuntu /bin/bash

这条命令将会启动一个新的名为 my_container 的容器,这个容器基于 Ubuntu 镜像并且会启动 Bash shell。

在这章中,我们将主要探讨 Docker 镜像:用来启动容器的基石。将会学习很多关于 Docker 镜像的知识,比如,什么是镜像,如果对镜像进行管理,如何修改镜像,

以及如何创建、存储和共享自己的镜像。还会介绍用来存储镜像的仓库和用来存储仓库的 Registry。

4.1 什么是 Docker 镜像

Docker 镜像是由文件系统叠加而成。最底端是一个引导文件系统,即 bootfs,跟典型的 Linux/Unix 的引导文件系统很像。

Docker 用户几乎永远不会和引导文件系统有什么交互。当一个容器启动后,它将被移到内存中,而引导文件系统则被卸载,以留出更多的内存供 initrd 磁盘镜像使用。

到目前位置,Docker 看起来还很像一个典型的 Linux 虚拟化栈。实际上,Docker 镜像的第二层是 root 文件系统 rootfs,它位于引导文件系统之上。

rootfs 可以是一种或多种操作系统(如 Debain 或者 Ubuntu 文件系统)。

在传统的 Linux 引导过程中,root 文件系统会最先以只读的方式加载,当引导结束并完成了完整性检查后,它才会被切换为读写模式。但是在 Docker 里,root 文件系统

永远只能是只读状态,并且 Docker 利用联合加载技术又会在 root 文件系统层上加载更多的只读文件系统。

联合加载指的是一次同时加载多个文件系统,但是在外面看起来只能看到一个文件系统。联合加载会将各层文件系统叠加到一起,这样最终的文件系统会包含所有底层的文

件和目录。

Docker 将这样的文件系统称为镜像。一个镜像可以放到另一个镜像的顶部。位于下面的镜像称为父镜像,可以以此类推,直到镜像栈的最底部,最底部的镜像称为基础

镜像。最后,当从一个镜像启动时,Docker 会在该镜像的最顶层加载一个读写文件系统。我们在Docker 中运行的程序就是在这个读写层中执行的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uh0vyMPk-1676705738847)(images/VNQiip3c76rT6Cbt0UHt0mrxe0GvKHPslx2hocsungY.png)]

当 Docker 第一次启动一个容器时,初始的读写层是空的。当文件系统发生变化时,这些变化都会应用到这一层上。比如,如果想修改一个文件,这个文件首先会从该读写

下面的只读层复制到该读写层。该文件的只读版本依然存在,但是已经被读写层的该文件副本所隐藏。

通常这种机制被称为写时复制,这也是使 Docker 如此强大的技术之一。每个只读镜像层都是只读的,并且以后永远不会变化。

当创建一个新容器时,Docker 会构建出一个镜像栈,并在栈的对顶端添加一个读写层。这个读写层再加上其下面的镜像层以及一些配置数据,就构成了一个容器。

4.2 列出 Docker 镜像

可以使用 docker images 命令来列出 Docker 主机上可用的镜像

$ sudo docker images REPOSITORY TAG IMAGE ID CREATED SIZE ubuntu latest 6b7dfa7e8fdb 5 weeks ago 77.8MB

可以看到已经获取了一个镜像列表,他们都源于名为 ubuntu 的仓库。那么,这些镜像是从何而来的呢?我们执行 docker run

命令时,同时进行了镜像下载。

注意:本地镜像都保存在 Docker 宿主机的 /var/lib/docker 目录下。每个镜像都保存在 Docker 所采用的存储驱动目录下。如 aufs 或者 devicemapper。也可以在 /var/lib/docker/containers 目录下面看到所有的容器。

镜像从仓库下载下来。镜像保存在仓库中,而仓库存在于 Registry 中。默认的 Registry 是由 Docker 公司运营的公共 Registry 服务,即 Docker Hub。Docker Registry 的也是开源的,也可以允许自己私有的 Registry。

在 Docker Hub (或者用户自己运营的 Registry)中,镜像是保存仓库中的。可以将镜像仓库想象为类似 Git 仓库的东西。它包括镜像,层以及关于镜像的元数据。

每个镜像仓库都可以存放很多镜像(比如,Ubuntu 仓库报告了 Ubuntu 18.04、20.04 镜像)。

拉取镜像

$ sudo docker pull ubuntu:latest latest: Pulling from library/ubuntu 6e3729cf69e0: Pull complete Digest: sha256:27cb6e6ccef575a4698b66f5de06c7ecd61589132d5a91d098f7f3f9285415a9 Status: Downloaded newer image for ubuntu:latest docker.io/library/ubuntu:latest

这里使用 docker pull 命令来拉取 ubuntu 仓库中的 ubuntu:latest 镜像。

再来看看 docker images 命令现在会显示什么结果

sudo docker images 4.3 拉取镜像

用 docker run 命令从镜像启动一个容器时,如果该镜像不在本地,Docker 会先从 Docker Hub 上下载该镜像。

如果没有指定具体的镜像标签,那么 Docker 会自动下载 latest 标签的镜像。

例如,如果本地宿主机上还没有 ubuntu:latest 镜像,如下所示代码将下载该镜像

**docker run 和 默认的 latest 标签 **

sudo docker run -t -i --name next_container ubuntu /bin/bash

也可以通过 docker pull 命令将镜像拉到本地。

使用 docker pull 命令可以节省从一个新镜像启动一个容器所需的时间。

拉取 fedora 镜像

sudo docker pull fedora:20

可以使用 docker images 命令查看已经下载到本地 Docker 宿主机上

查看 fedora 镜像

sudo docker images fedora

拉取带标签的 fedora 镜像

sudo docker pull fedora:21 4.4 查找镜像

通过 docker search 命令查找所有 Docker Hub 上公共的可用镜像

查找镜像

sudo docker search puppet NAME DESCRIPTION STARS OFFICIAL AUTOMATED puppet/puppetserver A Docker Image for running Puppet Server. Wi… 113 puppet/puppetdb A Docker image for running PuppetDB 37 puppet/puppetboard The Puppet Board dashboard for PuppetDB 20 ...

提示 也可以在 Docker Hub 网站上在线查找可用镜像 docker search puppet 命令在 Docker Hub 上查找所有带 puppet 的镜像,该命令完成镜像查找并返回下面信息:

仓库名镜像描述用户评价 ---- 反应一个镜像的受欢迎程度是否官方自动构建 ---- 表示该镜像是由 Docker Hub 的自动构建流程创建的

从 docker search puppet 的结果中拉取一个镜像

拉取 puppet/puppetserver 镜像

sudo docker pull puppet/puppetserver

上面命令将会下载 puppet/puppetserver 镜像到本地(这个镜像里预装了 puppet 服务器)

接着就可以用这个镜像构建一个容器了

从 puppetserver 镜像创建一个容器

sudo docker run -t -i puppet/puppetserver /bin/bash 4.5 构建镜像

拉取已经构建好的带有定制内容的 Docker 镜像后,修改了镜像,如何更新和管理这些镜像呢?构建 Docker 镜像有以下两种方法:

使用 docker commit 命令使用 docker build 命令和 Dockerfile 文件。

推荐使用 Dockerfile 的方式构建 Docker 镜像。

注意:一般来说,不是真正“创建”新镜像,而是基于一个已有的基础镜像,如 ubuntu 或 federa 等,构建新镜像而已。如果真的想从零构建一个全新的镜像,可以参考 https://docks.docker.com/articles/baseimages

4.5.1 创建 Docker Hub 账号

构建镜像中很重要的一环就是如何共享和发布镜像。可以将镜像推送到 Docker Hub 或者用户自己私有 Registry 中。需要在 Docker Hub 上创建一个账号再登录到 Docker Hub

登录到 Docker Hub

sudo docker login 4.5.2 用 Docker 的 commit 命令创建镜像

先创建一个容器,并在容器里做出修改,最后再将修改提交为一个新镜像

创建一个要进行修改的定制容器

sudo docker run -t -i ubuntu /bin/bash root@0c038cfe9e18:/#

接下来,在容器中安装 Apache

root@0c038cfe9e18:/# apt-get -yqq update ... root@0c038cfe9e18:/# apt-get -y install apache2 ...

启动了一个容器,并在里面安装了 Apache。将这个容器作为一个 Web 服务器来运行,所以想把它的当前状态保存下来。这样就不必每次都创建一个新容器并再次在里面安装 Apache 了。为了完成此项工作,需要先使用 exit 命令从容器退出,在运行 docker commit 命令

提交定制容器

sudo docker commit 0c038cfe9e18 ubuntu/apache2 sha256:a982b4ba932a7be8eea058f63d76117f6bbb1bc6d7ba528aab7b7f7e8429d319

上面 docker commit 命令中,指定了要提交的修改过的容器 ID(可以通过 docker ps -l -q 命令得到刚创建的容器 ID)以及一个目标镜像仓库和镜像名,这里是 ubuntu/apache2。需要注意的是,docker commit 提交的只是创建容器的镜像与容器的当前状态之间的差异部分,这使得该更新非常轻量。

检测新检测的镜像

sudo docker images ubuntu/apache2 REPOSITORY TAG IMAGE ID CREATED SIZE ubuntu/apache2 latest a982b4ba932a 43 seconds ago 227MB

也可以在提交镜像时指定更多的数据(包括标签)来详细描述所做的修改。

提交另一个新的定制容器

sudo docker commit -m "A new custom image" -a "sar" 0c038cfe9e18 ubuntu/apache2:webserver sha256:f55a1c33c1d7ae02f2f85cd2e809724eca09419a89da799d49176c7f32acee56

上面命令中,指定了更多信息,-m 选项用来指定创建的镜像提交信息,-a 选项指定该镜像的作者信息,接着指定了想要提交的容器的ID。最后的 ubuntu/apache2:webserver 指定了镜像的用户名和仓库名,并为该镜像增加了一个 webserver 标签。

可以用 docker inspect 命令来查看新创建的镜像的详细信息

查看提交的镜像的详细信息

sudo docker inspect ubuntu/apache2:webserver [ { "Id": "sha256:f55a1c33c1d7ae02f2f85cd2e809724eca09419a89da799d49176c7f32acee56", "RepoTags": [ "ubuntu/apache2:webserver" ], "RepoDigests": [], "Parent": "sha256:6b7dfa7e8fdbe18ad425dd965a1049d984f31cf0ad57fa6d5377cca355e65f03", "Comment": "A new custom image", "Created": "2023-02-17T07:17:52.475010058Z", "Container": "0c038cfe9e18236e0d11a30d821784a48359efdb70ac691a6d96ebc1cf282e2e", "ContainerConfig": { "Hostname": "0c038cfe9e18", "Domainname": "", "User": "", "AttachStdin": true, "AttachStdout": true, "AttachStderr": true, "Tty": true, "OpenStdin": true, "StdinOnce": true, "Env": [ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" ], "Cmd": [ "/bin/bash" ], "Image": "ubuntu", "Volumes": null, "WorkingDir": "", "Entrypoint": null, "OnBuild": null, "Labels": {} }, "DockerVersion": "20.10.21", "Author": "sar", "Config": { "Hostname": "0c038cfe9e18", "Domainname": "", "User": "", "AttachStdin": true, "AttachStdout": true, "AttachStderr": true, "Tty": true, "OpenStdin": true, "StdinOnce": true, "Env": [ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" ], "Cmd": [ "/bin/bash" ], "Image": "ubuntu", "Volumes": null, "WorkingDir": "", "Entrypoint": null, "OnBuild": null, "Labels": {} }, "Architecture": "amd64", "Os": "linux", "Size": 227361419, "VirtualSize": 227361419, "GraphDriver": { "Data": { "LowerDir": "/var/lib/docker/overlay2/abbaa48a8255eb1b3fd747f983154ae6f572175c45f00c0def0acc5bac44f4f8/diff", "MergedDir": "/var/lib/docker/overlay2/e010c1a5d80ea0e3442a34012bd396e11442350b62abf01a7d563042558f0f72/merged", "UpperDir": "/var/lib/docker/overlay2/e010c1a5d80ea0e3442a34012bd396e11442350b62abf01a7d563042558f0f72/diff", "WorkDir": "/var/lib/docker/overlay2/e010c1a5d80ea0e3442a34012bd396e11442350b62abf01a7d563042558f0f72/work" }, "Name": "overlay2" }, "RootFS": { "Type": "layers", "Layers": [ "sha256:6515074984c6f8bb1b8a9962c8fb5f310fc85e70b04c88442a3939c026dbfad3", "sha256:5a9d75687e1be1431aab443449b39894d846dd50c1b2f33a2b55f638de6f34e5" ] }, "Metadata": { "LastTagTime": "2023-02-17T15:17:52.930905199+08:00" } } ]

如果想从刚创建对策新镜像运行一个容器,可以使用 docker run 命令

从提交的镜像运行一个新容器

sudo docker run -t -i ubuntu/apache2:webserver /bin/bash

上面命令用了完整的标签 ubuntu/apache2:webserver 来指定这个镜像。

4.5.3 用 Dockerfile 构建镜像

推荐使用 Dockerfile 文件和 docker build 命令来构建镜像。Dockerfile 使用基本的基于 DSL 语法的命令来构建一个 Docker 镜像。推荐使用 Dockerfile 方法来代替 docker commit 命令,因为通过前者来构建镜像更具备可重复性、透明性以及幂等性。一旦有了 Dockerfile,就可以使用 docker build 命令基于 Dockerfile中的指定构建一个新的镜像。

第一个 Dockerfile

创建一个目录并在里面创建初始的 Dockerfile。目的是创建一个包含简单 web 服务器的 Docker 镜像

创建一个示例仓库

mkdir static_web cd static_web touch Dockerfile

上面命令创建了一个名为 static_web 的目录用来保存 Dockerfile,这个目录就是我们的构建环境, Docker 则称此环境为上下文或者构建上下文。Docker 会在构建镜像时将构建上下文和该上下文的文件和目录上传到 Docker 守护进程。这样 Docker 守护进程就能直接访问用户想在镜像中存储的任何代码、文件或者其他数据。作为开始,还创建了一个空 Dockerfile,下面通过一个例子来看看如何通过 Dockerfile 构建一个能作为 Web 服务器的 Docker 镜像。

***我们的第一个 Dockerfile ***

# Version: 0.0.1 FROM ubuntu:20.04 MAINTAINER sar "[email protected]" RUN apt-get update && apt-get install -y nginx RUN echo 'Hi, I am in your container' \ >/usr/share/nginx/html/index.html EXPOSE 80

该 Dockerfile 由一系列指令和参数组成,每条指令、如 FROM,都必须为大写字母,且后面要跟随一个参数:FROM ubuntu:20.04。Dockerfile 中的指令会按顺序从上到下指向,所以应该根据需要合理安排指令的顺序。每条指令都会创建一个新的镜像层并对镜像进行提交。Docker 大体上按照如下流程执行 Dockerfile 中的指令。

Docker 从基础镜像运行一个容器执行一条指令,对容器做出修改。执行类似 docker commit 的操作,提交一个新的镜像层次Docker 基于刚提交的镜像运行一个新容器指向 Dockerfile 中的下一条指令,直到所有指令都执行完毕 如果用户的 Dockerfile 由于某些原因(如某条指令失败了)没有正常结束,那么用户将得到一个可以使用的镜像。这对调试非常有帮助:可以基于该镜像运行一个具备交互功能的容器,使用最后创建的镜像对为什么用户的指令会失败进行调试。

注意:Dockerfile 也支持注释。以 # 开头的行都会被认为是注释

每个 Dockerfile 的第一条指令必须是 FROM。FROM 指令指定一个已经存在的镜像,后续指令都将基于该镜像进行,这个镜像被称为基础镜像。在上面的示例中,指定了 ubuntu:20.04 作为新镜像的基础镜像。基于这个 Dockerfile 构建的新镜像将以 Ubuntu 20.04 操作系统为基础。在运行一个容器时,必须要指明是基于哪个基础镜像进行构建。MAINTAINER 指令告诉 Docker 该镜像的作者是谁,以及作者的电子邮件地址。最后指定了两条 RUN 值令。RUN 指令会在当前镜像中运行指定的命令。默认情况下,RUN 指令会在 shell 里使用命令包装器 /bin/sh -c 来执行。如果是在一个不支持 shell 的平台上或者不希望在 shell中运行,也可以使用 exec 格式的 RUN 指令

*** exec 格式的 RUN 指令***

RUN [ “apt-get”, " install", “-y”, “nginx” ]

在上面这种方式中,使用一个数组来指定要运行的命令和传递给命令的每个参数。接着设置了 EXPOSE 指令,这条指令告诉 Docker 该容器内的应用程序将会使用容器的指定端口。这并不意味着可以自动访问任意容器运行中服务的端口。出于安全原因,Docker 并不会自动打开该端口,而是需要用户使用 docker run 运行容器时来指定需要打开哪些端口。可以指定多个 EXPOSE 指令来向外部公开多个端口。

4.5.5 基于 Dockerfile 构建新镜像

执行 docker build 命令时,Dockerfile 中的所有指令都会被执行并且提交,并且在该命令成功结束后返回一个新镜像。

***运行 Dockerfile ***

sudo docker build -t="sar/static_web" . Sending build context to Docker daemon 2.048kB Step 1/5 : FROM ubuntu:20.04 20.04: Pulling from library/ubuntu b549f31133a9: Pull complete Digest: sha256:4a45212e9518f35983a976eead0de5eecc555a2f047134e9dd2cfc589076a00d Status: Downloaded newer image for ubuntu:20.04 ---> e40cf56b4be3 Step 2/5 : MAINTAINER sar "[email protected]" ---> Running in 933215eb39e4 Removing intermediate container 933215eb39e4 ---> df2e0c365c98 Step 3/5 : RUN apt-get update && apt-get install -y nginx ---> Running in 0410dc3d2945 Removing intermediate container 0410dc3d2945 ---> f3a5f48a8ccd Step 4/5 : RUN echo 'Hi, I am in your container' >/usr/share/nginx/html/index.html ---> Running in 26abb5b57315 Removing intermediate container 26abb5b57315 ---> 1b29e2ec5648 Step 5/5 : EXPOSE 80 ---> Running in c43521c79c73 Removing intermediate container c43521c79c73 ---> 55acde2328f4 Successfully built 55acde2328f4 Successfully tagged sar/static_web:latest

使用 docker build 命令来构建新镜像。使用 -t 选项为新镜像设置了仓库和名称,本例中仓库为 sar 镜像名为 static_web。

强烈建议为自动的镜像设置合适的名字以方便追踪和管理。

也可以在构建镜像的过程中为镜像设置一个标签,其使用方法为“镜像名:标签”

在构建时为镜像设置标签

sudo docker build -t="sar/static_web:v1" .

提示:如果没有定制任何标签,Docker 会自动为镜像设置一个 latest 标签

上面命令中最后的 “.” 告诉 Docker 到本地目录中去找 Dockerfile 文件。也可以指定一个 Git 仓库的源地址来指定 Dockerfile 的位置。

从 git 仓库构建 Docker 镜像

sudo docker build -t="sar/static_web:v1" \ [email protected]:sar/docker-static_web

提示:如果在构建上下文的根目录下存在以.dockerignore 命名的文件的话,那么该文件内容会被按行进行分割,每一行都是一条文件过滤匹配模式。与.gitignore 文件类似,该文件用来设置哪些文件不会被当作构建上下文的一部分,因此可以防止它们被上传到 Docker 守护进程中去。

可以看到 Dockerfile 中的每条指令会被顺序执行,而且作为构建过程的最终结果,返回了新镜像的 ID,即 55acde2328f4。构建的每一步及其对应指令都会独立运行,并且在输出最终镜像 ID 之前,Docker 会提交每步的构建结果。

4.5.5 指令失败时会怎样

前面介绍了一个指令失败时将会怎样,下面看一个例子:假设在第 4 步中将软件包的名字弄错了,比如写成了 ngin

再次运行一遍构建过程并看看当指令失败时会怎样。

管理失败的指令

sudo docker build -t="sar/static_web" . [sudo] password for sar: Sending build context to Docker daemon 2.048kB Step 1/5 : FROM ubuntu:20.04 ---> e40cf56b4be3 Step 2/5 : MAINTAINER sar "[email protected]" ---> Using cache ---> df2e0c365c98 Step 3/5 : RUN apt-get update && apt-get install -y ngin ---> Running in 65031aca8e64 Reading package lists... Reading package lists... Building dependency tree... Reading state information... E: Unable to locate package ngin The command '/bin/sh -c apt-get update && apt-get install -y ngin' returned a non-zero code: 100

这时需要调试一下失败。用 docker run 命令来基于这次构建到目前为止已经成功的最后一步创建一个容器。

在上面例子中,使用的镜像 ID 是 df2e0c365c98,

基于最后的成功步骤创建新容器

sudo docker run -t -i df2e0c365c98 /bin/bash root@c867c1c5d8b2:/#

这时在这个容器中可以再次运行 apt-get install -y ngin,并指定正确的包名,或者通过进一步的调试来找出到底哪里出错了。

一旦解决了这个问题,就可以退出容器,使用正确的包名修改 Dockerfile 文件,之后再尝试进行构建。

4.5.6 Dockerfile 和构建缓存

由于每一步的构建过程都会将结果提交为镜像,所有 Docker 的构建镜像过程显得非常聪明。它会将之前的镜像层看作缓存。比如,在前面的调试例子中,我们不需要在第 1 步到第 3 步之间进行任何修改,因此 Docker 会将之前构建的镜像当做缓存并作为新的开始点。实际上,当再次进行构建时,Docker 会直接从第 4 步开始。当之前的构建步骤没有变化时,这会节省大量的时间。如果真的在第 1 步到第 3 步之间做了修改,Docker 则会从第一条发生了变化的指令开始。然而,有些时候需要确保构建过程不会使用缓存。比如,如果已经缓存了前面的第 3 步,即 apt-get update 那么 Docker 将不会在此刷新 APT 包缓存。这时用户可能需要取得每个包的最新版本。要想略过缓存功能,可以使用 docker build 的 --no-cache 标志,

*** 忽略 Dockerfile 的构建缓存 ***

sudo docker build --no-cache -t="sar/static_web" . 4.5.7 基于构建缓存的 Dockerfile 模板

构建缓存带来的一个好处就是,可以实现简单的 Dockerfile 模板(比如在 Dockerfile 文件顶部增加仓库或者更新包,从而尽可能确保缓存命中)。我们一般都会在自己的 Dockerfile 文件顶部使用相同的指令集模板,比如对 Ubuntu,使用下面所示模板。

Ubuntu 系统的Dockerfile模板

FROM ubuntu:20.04 MAINTAINER sar "[email protected]" ENV REFRESHED_AT 2023-02-18 RUN apt-get -qq update

上面 Dockerfile 首先通过 FROM 指令为新镜像设置了一个基础镜像 ubuntu:20.04。接着使用 MAINTAINER 指令添加维护者的联系信息;接下来使用 ENV 指令在镜像中设置环境变量。在上面例子中,通过 ENV 指令来设置一个名为 REFRESHED_AT 的环境变量,这个环境变量用来表明该镜像模板最后的更新时间。最后,使用了 RUN 指令来运行 apt-get -qq update 命令,该指令运行时将会刷新 APT 包缓存,用来确保能将要安装的每个软件包更新到最新版本中。有了这个模板,如果想刷新一个构建,只需要修改 ENV 指令中的日期。这使 Docker 在命中 ENV 指令时开始重置这个缓存,并运行后续指令而无须依赖该缓存。也就是说, RUN apt-get update 这条指令将会被再次执行,包缓存也将会被刷新为最新内容。可扩展次模板,比如适配到不同的平台或者添加额外的需求。比如,可将上面示例改为支持一个 fedora 镜像。

*** Fedora Dockerfile 模板***

FROM ubuntu:20.04 MAINTAINER sar "[email protected]" ENV REFRESHED_AT 2023-02-18 RUN yum -q makecache 4.5.8 查看新镜像

可以使用 docker images 命令来查看新构建的镜像

列出新的 Docker 镜像

sudo docker images ubuntu:static_web REPOSITORY TAG IMAGE ID CREATED SIZE ubuntu static_web 5a6d038755d2 4 weeks ago 174MB

如果想更深入探求镜像是如何构建出来的,可以使用 docker history m命令。

使用 docker history 命令

sudo docker history 5a6d038755d2 IMAGE CREATED CREATED BY SIZE COMMENT 5a6d038755d2 4 weeks ago /bin/sh -c #(nop) EXPOSE 80 0B c947b068c315 4 weeks ago /bin/sh -c echo 'Hi, I am in your container'… 27B d1efd38f7785 4 weeks ago /bin/sh -c apt-get update && apt-get install… 96.3MB efe03cf82c02 4 weeks ago /bin/sh -c #(nop) MAINTAINER sar "6451085@q… 0B 6b7dfa7e8fdb 2 months ago /bin/sh -c #(nop) CMD ["bash"] 0B 2 months ago /bin/sh -c #(nop) ADD file:481dd2da6de715252… 77.8MB 4.5.9 从新镜像启动容器

可以基于新构建的镜像启动一个新容器,来检查构建工作是否一切正常。

从新镜像启动一个容器

sudo docker run -d -p 80 --name static_web ubuntu:static_web nginx -g "daemon off;" bc3ec35dabb14c5a600dca86636680d525dbb05baf84ec0d7269e63c11485a33

使用 docker run 命令,基于刚才构建的镜像的名字,启动了一个名为 static_web 的新容器。同时指定了 -d 选项,告诉 Docker 以分离的方式在后台运行。这种方式非常时候运行类似 Nginx 守护进程这样需要长时间运行的进程。同时还指定了需要在容器中运行的命令:nginx -g “daemon off;”。这将以前台运行的方式启动 Nginx,来作为我们的 Web 服务器。还使用了一个新的 -p 标志,该标志用来控制 Docker 在运行时应该公开哪些网络端口给外部(宿主机)。运行一个容器时, Docker 可以通过两种方法来在宿主机上分配端口。

Docker 可以在宿主机上随机选择一个位于 32768 ~ 61000 的一个比较大的端口来映射到容器中的 80 端口上。可以在 Docker 宿主机中指定一个具体的端口号来映射到容器中的 80 端口上。

docker run 命令将在 Docker 宿主机上随机打开一个端口,这个端口会连接到容器中的 80 端口上。使用 docker ps 命令来看一下容器的端口分配情况。

***查看 Docker 端口映射情况 ***

sudo docker ps -l CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES bc3ec35dabb1 ubuntu:static_web "nginx -g 'daemon of…" 22 seconds ago Up 21 seconds 0.0.0.0:32768->80/tcp, :::32768->80/tcp static_web

可以看到,容器中的 80 端口被映射到了宿主机的 49154 上。也可以通过 docker port 来查看容器的端口映射情况。

*** docker port 命令***

sudo docker port bc3ec35dabb1 80

-p 选项还为我们将容器端口向宿主机公开时提供了一定的灵活性。比如,可以指定将容器中的端口映射到 Docker 宿主机的某一特定端口上。

通过 -p 选项映射到特定端口

sudo docker run -d -p 80:80 --name static_web ubuntu:static_web nginx -g "daemon off;"

上面命令会将容器内的 80 端口绑定到本地宿主机的 80 端口上。必须非常小心地使用这种直接绑定的做法:如果要运行多个容器,只有一个容器能成功地将端口绑定到本地宿主机上,这将会限制 Docker 的灵活性。为了避免这个问题,可以将容器内的端口绑定到不同的宿主机端口上去,

绑定不同的端口

sudo docker run -d -p 8080:80 --name static_web ubuntu:static_web nginx -g "daemon off;"

这条命令会将容器中的80端口绑定到宿主机的8080端口上。也可以将端口绑定限制在特定的网络接口(即 IP 地址)上。

绑定到特定的网络接口

sudo docker run -d -p 127.0.0.1:8080:80 --name static_web ubuntu:static_web nginx -g "daemon off;"

将容器内的 80 端口绑定到了本地宿主机的 127.0.0.1 这个 IP 的 8080 端口上。也可以使用类似的方式将容器内的 80 端口绑定到宿主机的随机端口上.

绑定到特定的网络接口的随机端口

sudo docker run -d -p 127.0.0.1::80 --name static_web ubuntu:static_web nginx -g "daemon off;"

这里,并没有指定具体要绑定的宿主机上的端口号,只是指定了一个 IP 地址 127.0.0.1,这时我们可以使用 docker inspect 或者docker port 命令来查看容器内的 80 端口具体被绑定到了宿主机的哪个端口上。

注意:也可以通过在端口绑定时使用 /udp 后缀来指定 UDP 端口

Docker 还提供了一个更简单的方式,即 -P 参数,该参数可以用来对外公开在 Dockerfile 中通过 EXPOSE 指令公开的所有端口。

使用 docker run 命令对外公开端口

sudo docker run -d -P --name static_web ubuntu:static_web nginx -g "daemon off;"

该命令会将容器内的 80 端口对本地宿主机公开,并且绑定到宿主机的一个随机端口上。该命令会将用来构建该镜像的 Dockerfile 文件中的 EXPOSE 指令指定的其他端口也一并公开。有了这个端口号,就可以使用本地宿主机的 IP 地址或者 127.0.0.1 的 localhost 连接到运行中的容器,查看 web 服务器内容了。

*** 使用 curl 连接到容器***

curl localhost:49154 4.5.10 Dockerfile 指令

Dockerfile 中常用的指令有:CMD、ENTRYPOINT、ADD、COPY、VOLUME、WORKDIR、USER、ONBUILD、LABEL、STOPSIGNAL、ARG 和 ENV 等。

可以在 http://docs.docker.com/reference/builder 查看 Dockerfile 中可以使用的全部指令清单。

1.CMD

CMD 指令用于指定一个容器启动时要运行的命令。这有点儿类似于 RUN 指令,只是 RUN 指令是指定镜像被构建时要运行的命令,而 CMD 是指定容器被启动时要运行的命令。这和使用 docker run 命令启动容器时指定要运行的命令非常类似。

指定要运行的特定命令

sudo docker run -i -t ubuntu:static_web /bin/true

可以认为上面所示命令和在 Dockerfile 中使用下面 CMD 指令是等效的

使用 CMD 指令

CMD ["/bin/true"]

当然也可以为要运行的命令指定参数

***给 CMD 指令传递参数 ***

CMD ["/bin/bash", "-l"]

上面将 -l 标志传递给了 /bin/bash 命令

警告:需要注意的是,要运行的命令是存放在一个数组结构中的。这将告诉 Docker 按指定的原样来运行命令。当然也可以不使用数组而是指定 CMD 指令,这时候 Docker 会在指定的命令钱加上/bin/sh -c 。这在执行该命令的时候可能会导致意料之外的行为,所以 Docker 推荐一直使用以数组语法来设置要执行的命令。

2.ENTRYPOINT

ENTRYPOINT 指令与 CMD 指令非常类似,也很容易和 CMD 指令弄混。这两条指令有什么区别呢?我们可以在 docker run 命令行中覆盖 CMD 指令。有时候,我们希望容器会按照我们想象的那样去工作,这时候 CMD 就不太合适了。而 ENTRYPOINT 指令提供的命令则不容易在启动容器时被覆盖。实际上,docker run 命令行中指定的任何参数都会被当做参数再次传递给 ENTRYPOINT 指令中指定的命令。

指定ENTRYPOINT指令

ENTRYPOINT ["/usr/sbin/nginx"]

类似 CMD 指令,也可以在该指令中通过数组的方式为命令指定相应的参数

ENTRYPOINT ["/usr/sbin/nginx", "-g", "daemon off;"] 3.WORDIR

WORKDIR 指令用来在从镜像创建一个新容器时,在容器内部设置一个工作目录,ENTRYPOINT 和/或 CMD 指定的程序会在这个目录下执行。

我们可以使用该指令为 Dockerfile 中后续的一系列指令设置工作目录,也可以为最终的容器设置工作目录。如下代码为特定的指令设置不同的工作目录。

使用WORKDIR指令

WORKDIR /opt/webapp/db RUN bundle install WORKDIR /opt/webapp ENTRYPOINT [ "rackup" ]

上面代码将工作目录切换为 /opt/webapp/db 后运行了 boundle install 命令,之后又将工作目录设置为 /opt/webapp,最后设置了 ENTRYPOINT 指令来启动 rackup 命令。

可以通过 -w 标志在运行时覆盖工作目录

覆盖工作目录

sudo docker run -ti -w /var/log ubuntu pwd /var/log

上面命令会将容器内的工作目录设置为 /var/log

4.ENV

ENV 命令用来在镜像构建过程中设置环境变量

在 Dockerfile文件中设置环境变量

ENV RVM_PATH /home/rvm

这个新的环境变量可以在后续的任何 RUN 指令中使用,这就如同在命令前面指定了环境变量前缀一样。

为 RUN 指令设置前缀

RUN gem install unicorn 5.USER 6.VOLUME 7.ADD 8.COPY 9.LABEL 10.STOPSIGNAL 11.ARG 12.ONBUILD 4.6 将镜像推送到 Docker Hub

镜像构建完毕后,可以将它上传到 Docker Hub 上去。通过 docker push 命令将镜像推送到 Docker Hub。

尝试推送 root 镜像

sudo docker push static_web 4.7 删除镜像

如果不需要一个镜像了,可以将它删除。可以使用 docker rmi 命令来删除一个镜像。

删除 Docker 镜像

sudo docker rmi ubuntu:static_web

删除了 ubuntu:static_web 镜像。在这里也可以看到 Docker 的分层文件系统:每一个 Deleted: 行都代表一个镜像层被删除。

同时删除多个 Docker 镜像

sudo docker rmi ubuntu:static_web ubuntu:vim

删除所有镜像

sudo docker rmi `docker images -a -q` 4.8 运行自己的 Docker Registry

4.9 其他可选 Registry 服务



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3